package com.jj.core.client.impl;

import com.alibaba.fastjson.JSON;
import com.jj.aop.annotation.RPC;
import com.jj.core.client.Client;
import com.jj.core.client.ClientManager;
import com.jj.core.client.MsgBuilder;
import com.jj.message.ProtoFormat;
import com.jj.message.battle.UserMessage;
import com.jj.net.kcp.KcpChannelManager;
import com.jj.net.tcp.TcpAccepterManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.collections4.map.LRUMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Copyrights: QidianGroup@JJ
 *
 * @author: LuHan
 * @date: 2018/1/15, 13:34
 */
@Component
public class ClientManagerImpl implements ClientManager, MethodInterceptor {
    private static Logger logger = LoggerFactory.getLogger(ClientManagerImpl.class);

    private LRUMap<Long, Client> clientMap = new LRUMap<>(50*1024);

    private MsgBuilder msgBuilder = (MsgBuilder) Enhancer.create(MsgBuilder.class, this);

    @Autowired
    private KcpChannelManager kcpChannelManager;

    @Autowired
    private TcpAccepterManager tcpAccepterManager;

    @Override
    public synchronized Client get(Long id) {

        Client client = clientMap.get(id);
        if(client == null){
            client = new ClientImpl(kcpChannelManager,tcpAccepterManager, id);
            clientMap.put(id, client);
        }
        return client;

    }

    @Override
    public synchronized Client remove(Long id) {
        return clientMap.remove(id);
    }

    @Override
    public synchronized void reset(){
        clientMap.clear();
        logger.debug("clear all client!");
    }

    public MsgBuilder msg(){
        return msgBuilder;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        logger.debug("before invoke {}.{} with {}", o, method, objects);
        //return methodProxy.invokeSuper(o, objects);
        //build message

        Object[] convertedObjArray = convertParams(objects);
        RPC rpc = method.getAnnotation(RPC.class);
        if(rpc != null){
            UserMessage userMsg = new UserMessage();
            userMsg.setProtocolFormat(ProtoFormat.JSON);
            userMsg.setMsgID((short) rpc.id());
            if(rpc.id() == -1 && (rpc.args() == null || rpc.args().length == 0)){
                if(objects.length == 1)
                {
                    userMsg.setJsonMsg(JSON.toJSONString(objects[0]));
                }else{
                    userMsg.setJsonMsg(JSON.toJSONString(objects));
                }
            }else if(rpc.args() != null && rpc.args().length != 0){
                Map args = new HashMap();
                String[] argNames = rpc.args();
                for(int i = 0; i< argNames.length; i++){
                    args.put(argNames[i], convertedObjArray[i]);
                }
                userMsg.setJsonMsg(JSON.toJSONString(args));
            }else{
                //generic call
                userMsg.setJsonMsg(JSON.toJSONString(convertedObjArray));
            }

            return userMsg;
        }else{
            logger.warn("no annotations for {}", method);
        }
        return null;
    }

    private Object[] convertParams(Object[] parameters){
        List list = new LinkedList();
        if(parameters == null){
            return null;
        }else{
            for(Object obj : parameters){
                if(obj instanceof Map){
                    Map m = (Map) obj;
                    Map<String, Object> strKeyMap = new HashMap<>();
                    for(Object key : m.keySet()){
                        strKeyMap.put(""+key, m.get(key));
                    }
                    list.add(strKeyMap);
                }else{
                    list.add(obj);
                }
            }
            return list.toArray(new Object[0]);
        }
    }
}
